﻿using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

namespace UnityModule
{
    public sealed partial class AssetBundleComponent : SingletonBehaviour<AssetBundleComponent>
    {
        /// <summary>
        /// 加载异常表, key带hashcode
        /// </summary>
        private Dictionary<string, string> dicException = new Dictionary<string, string>();
        // 加载好的bundle列表,key带hashcode
        private Dictionary<string, LoadedAssetBundle> dicLoadedBundle = new Dictionary<string, LoadedAssetBundle>();
        // 正在加载bundle列表,key带hashcode
        private Dictionary<string, LoadingBundles> dicLoadingList = new Dictionary<string, LoadingBundles>();
        // 回调列表,key带hashcode
        private Dictionary<string, List<LoadBundleCallBackInfo>> dicCallBackList = new Dictionary<string, List<LoadBundleCallBackInfo>>();
        // 需要移除的键值，每次使用前要clear
        private List<string> keysToRemove = new List<string>();
        //bundle之间引用
        private Dictionary<string, int> bundleRef = new Dictionary<string, int>();
        public void Init()
        {
        }

        //private void Update()
        //{
        //    if(Input.GetKeyDown(KeyCode.Q))
        //    {
        //        foreach (var item in bundleRef)
        //        {
        //            LoadedAssetBundle dlTemp = GetLoadedAssetBundle(item.Key);
        //            if (dlTemp == null)
        //            {
        //                Debug.LogError(item.Key + " " + item.Value + "  null");
        //            }
        //            else
        //            {
        //                Debug.LogError(item.Key + " " + item.Value + "  " + dlTemp.refCount);
        //            }

        //            //if (dlTemp != null && dlTemp.refCount != item.Value)
        //            //{
        //            //    Debug.LogError(item.Key + " ====" + item.Value + "  ====" + dlTemp.refCount);
        //            //}
        //            if(item.Value < 0)
        //            {
        //                Debug.LogError("refCount" + item.Key);
        //            }
        //        }
        //    }
        //}

        /// <summary>
        /// 把bundle缓存到已加载表中
        /// </summary>
        /// <param name="abNameWithHash"></param>
        /// <param name="ab"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private LoadedAssetBundle CacheBundle(string abNameWithHash, AssetBundle ab, LoadOptions options)
        {
            LoadedAssetBundle ldTemp = new LoadedAssetBundle();
            ldTemp.abNameWithHash = abNameWithHash;
            ldTemp.assetBundle = ab;
            ldTemp.options = options;
            dicLoadedBundle[abNameWithHash] = ldTemp;
            return ldTemp;
        }

        /// <summary>
        /// 获取bundle信息
        /// </summary>
        /// <param name="abNameWithoutHash"></param>
        /// <returns></returns>
        private AssetBundleInfoV2 GetAssetBundleInfo(string abNameWithoutHash)
        {
            AssetBundleInfoV2 info = null;
            VersionComponent.Instance.CdnBundleInfos.TryGetValue(abNameWithoutHash, out info);
            return info;
        }

        /// <summary>
        /// 获取bundle信息
        /// </summary>
        /// <param name="abNameWithoutHash"></param>
        /// <returns></returns>
        private AssetBundleInfoV2 GetAssetBundleInfoWithHash(string abNameWithHash)
        {
            return GetAssetBundleInfo(VersionComponent.Instance.CdnBundleInfosWithHash[abNameWithHash]); ;
        }


        /// <summary>
        /// 判断最新bundle列表中是否存在bundle
        /// </summary>
        /// <param name="abName">不带hash</param>
        /// <returns></returns>
        public bool ContainsBundle(string abNameWithoutHash)
        {
            return VersionComponent.Instance.CdnBundleInfos.ContainsKey(abNameWithoutHash);
        }

        /// <summary>
        /// 获取加载好的bundle
        /// </summary>
        /// <param name="abNameWithHash"></param>
        /// <returns></returns>
        public LoadedAssetBundle GetLoadedAssetBundle(string abNameWithHash)
        {
            LoadedAssetBundle ldTemp = null;
            dicLoadedBundle.TryGetValue(abNameWithHash, out ldTemp);
            return ldTemp;
        }

        /// <summary>
        /// bundle和依赖全部加载好才返回true
        /// </summary>
        /// <param name="abName">ab名称带hashcode</param>
        /// <returns></returns>
        private bool IsBundleLoaded(AssetBundleInfoV2 bundleInfo)
        {
            LoadedAssetBundle ldBundle = null;
            ldBundle = GetLoadedAssetBundle(bundleInfo.abNameWithHash);
            if (null == ldBundle)
            {
                return false;
            }
            if (dicException.ContainsKey(ldBundle.abNameWithHash))
            {
                return true;
            }
            if (null != bundleInfo.de)
            {
                for (int i = bundleInfo.de.Length - 1; i >= 0; i--)
                {
                    ldBundle = GetLoadedAssetBundle(bundleInfo.de[i]);
                    if (null == ldBundle)
                    {
                        return false;
                    }
                    else
                    {
                        if (dicException.ContainsKey(ldBundle.abNameWithHash))
                        {
                            return true;
                        }
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 判断资源已经下载完成
        /// </summary>
        /// <param name="abNameWithoutHash"></param>
        /// <returns></returns>
        public bool IsBundleLoaded(string abNameWithoutHash)
        {
            AssetBundleInfoV2 temp = GetAssetBundleInfo(abNameWithoutHash);
            if (UpdateSettings.GameSetting.emResType == ResourceMode.EditorLocalRes)
            {
                return true;
            }

            if (null != temp && IsDownloadABExist(temp.abNameWithHash))
            {
                //已经加载好
                return true;
            }
            return false;
        }

        private void AddBundleRef(string abNameWithHash)
        {
            int count = 0;
            bundleRef.TryGetValue(abNameWithHash, out count);
            bundleRef[abNameWithHash] = count + 1;
            //if (abNameWithHash.Contains("sprite/hall_"))
            //{
            //    Debug.LogError("aaaa");
            //}
        }
        private void SubBundleRef(string abNameWithHash)
        {
            int count = 0;
            bundleRef.TryGetValue(abNameWithHash, out count);            
            bundleRef[abNameWithHash] = count - 1;
            if(bundleRef[abNameWithHash] < 0)
            {
                Debug.LogError("Error Ref" + abNameWithHash);
            }
        }
        /// <summary>
        /// 加载bundle
        /// </summary>
        /// <param name="abNameWithoutHash"></param>
        /// <returns></returns>
        public void GetBundle(string abNameWithoutHash, Action<LoadedAssetBundle> callBack, LoadOptions options)
        {
            AssetBundleInfoV2 temp = GetAssetBundleInfo(abNameWithoutHash);
            if (null == temp)
            {
                callBack(null);
                return;
            }
            if (IsBundleLoaded(temp))
            {
                //已经加载好
                callBack(GetLoadedAssetBundle(temp.abNameWithHash));
                return;
            }
            if (dicException.ContainsKey(temp.abNameWithHash))
            {
                callBack(null);
                return;
            }
            /**
            异步条件检查
             */
            options = CheckSync(options, temp);
            bool bHightPriority = ((options & LoadOptions.HightPriority) == LoadOptions.HightPriority) || options == LoadOptions.Scene;
            LoadedAssetBundle ldtemp = null;
            string[] de = temp.de;
            if (null != de)
            {
                for (int i = de.Length - 1; i >= 0; i--)
                {
                    ldtemp = GetLoadedAssetBundle(de[i]);

                    AddBundleRef(de[i]);
                    if (null != ldtemp)
                    {
                        ldtemp.refCount++;                        
                    }
                    else
                    {
                        if (bHightPriority)
                        {
                            AssetBundle tempBundle = AssetBundle.LoadFromFile(GetABPath(de[i]));
                            ldtemp = CacheBundle(de[i], tempBundle, options);
                            ldtemp.refCount++;
                        }
                        else
                        {
                            LoadBundleInternal(GetAssetBundleInfoWithHash(de[i]), options);
                        }
                    }
                }
            }
            ldtemp = GetLoadedAssetBundle(temp.abNameWithHash);
            if (null == ldtemp)
            {
                AddBundleRef(temp.abNameWithHash);

                if (bHightPriority)
                {
                    AssetBundle tempBundle = AssetBundle.LoadFromFile(GetABPath(temp.abNameWithHash));
                    ldtemp = CacheBundle(temp.abNameWithHash, tempBundle, options);
                    ldtemp.refCount++;
                }
                else
                {
                    if (!dicCallBackList.ContainsKey(temp.abNameWithHash))
                    {
                        dicCallBackList[temp.abNameWithHash] = new List<LoadBundleCallBackInfo>();
                    }
                    dicCallBackList[temp.abNameWithHash].Add(new LoadBundleCallBackInfo(temp, callBack));
                    LoadBundleInternal(temp, options);
                }

            }
            if (bHightPriority)
                callBack(ldtemp);
        }
        /// <summary>
        /// 检查是否有同步条件
        /// </summary>
        /// <param name="options"></param>
        /// <param name="temp"></param>
        /// <returns></returns>
        private LoadOptions CheckSync(LoadOptions options, AssetBundleInfo temp)
        {
            if ((options & LoadOptions.HightPriority) == LoadOptions.HightPriority)
            {
                //检测是否有同步条件
                bool canSync = true;
                string[] de = temp.de;
                //如果是边玩边下
                //if (temp.pathType == (byte)AssetBundleInfoPathType.Background)
                //{
                //如果没加载好,且正在加载表中,不可以异步
                if (!IsDownloadABExist(temp.abNameWithHash) || dicLoadingList.ContainsKey(temp.abNameWithHash))
                    canSync = false;
                if (canSync)
                {
                    if (null != de)
                    {
                        for (int i = de.Length - 1; i >= 0; i--)
                        {
                            if (!IsDownloadABExist(de[i]) || dicLoadingList.ContainsKey(de[i]))
                            {
                                canSync = false;
                                break;
                            }
                        }
                    }
                }
                //}
                //else
                //{
                //    if (dicLoadingList.ContainsKey(temp.abNameWithHash))
                //        canSync = false;
                //    if (canSync)
                //    {
                //        if (null != de)
                //        {
                //            for (int i = de.Length - 1; i >= 0; i--)
                //            {
                //                if (dicLoadingList.ContainsKey(de[i]))
                //                {
                //                    canSync = false;
                //                    break;
                //                }
                //            }
                //        }
                //    }
                //}
                if (!canSync)
                {
                    options = options ^ LoadOptions.HightPriority;
                }
            }

            return options;
        }

        /// <summary>
        /// 加入加载列表
        /// </summary>
        /// <param name="abName"></param>
        /// <param name="options"></param>
        private void LoadBundleInternal(AssetBundleInfoV2 bundleInfo, LoadOptions options)
        {
            if (IsAsyncLoading(bundleInfo.abNameWithHash))
                return;
            if (UpdateSettings.GameSetting.emResType == ResourceMode.LocalBundle)//if (ResourceLoader.Get().LOCAL_RES)
            {
                dicLoadingList[bundleInfo.abNameWithHash] = LoadingBundlesManager.Load(bundleInfo.abNameWithHash, GetABPath(bundleInfo.abNameWithHash), options, LoadingDone);
            }
            else
            {
                if (IsDownloadABExist(bundleInfo.abNameWithHash))
                {
                    dicLoadingList[bundleInfo.abNameWithHash] = LoadingBundlesManager.Load(bundleInfo.abNameWithHash, GetABPath(bundleInfo.abNameWithHash), options, LoadingDone);
                }
                else
                {
#if !UNITY_IOS || HUpdate
                    void DownloadComplete(string abname,string exception)
                    {
                        if (IsDownloadABExist(bundleInfo.abNameWithHash))
                        {
                            dicLoadingList[bundleInfo.abNameWithHash] = LoadingBundlesManager.Load(bundleInfo.abNameWithHash, GetABPath(bundleInfo.abNameWithHash), options, LoadingDone);
                        }
                        else
                        {
                            P.E("尝试下载后依旧无法加载资源：" + bundleInfo.abNameWithHash);
                            LoadingError(bundleInfo);
                        }
                    }
                    DownloadComponent.Instance.Download(bundleInfo, DownloadComplete, null);
#endif
                }
            }
        }
        private bool IsAsyncLoading(string abName)
        {
            return dicLoadingList.ContainsKey(abName);
        }
        /// <summary>
        /// bundle加载完成回调
        /// </summary>
        /// <param name="loadingBundle"></param>
        private void LoadingDone(LoadingBundles loadingBundle)
        {
            if (null != loadingBundle.request && null != loadingBundle.request.assetBundle)
            {
                LoadedAssetBundle ldBundle = CacheBundle(loadingBundle.abNameWithHash, loadingBundle.request.assetBundle, loadingBundle.options);
                ldBundle.refCount++;
            }
            else
            {
                if (null == GetLoadedAssetBundle(loadingBundle.abNameWithHash))
                    dicException[loadingBundle.abNameWithHash] = loadingBundle.abNameWithHash + "加载失败";
            }
            if (dicLoadingList.ContainsKey(loadingBundle.abNameWithHash)) dicLoadingList.Remove(loadingBundle.abNameWithHash);
            ExcuteBundleDone();
        }

        private void LoadingError(AssetBundleInfoV2 bundleinfo)
        {
            if (null == GetLoadedAssetBundle(bundleinfo.abNameWithHash))
                dicException[bundleinfo.abNameWithHash] = bundleinfo.abNameWithHash + "加载失败";
            if (dicLoadingList.ContainsKey(bundleinfo.abNameWithHash)) dicLoadingList.Remove(bundleinfo.abNameWithHash);


            ExcuteBundleDone();
        }


        private void ExcuteBundleDone()
        {
            keysToRemove.Clear();
            foreach (var item in dicCallBackList)
            {
                if (IsBundleLoaded(item.Value[0].bundleInfo) || dicException.ContainsKey(item.Key))
                {
                    keysToRemove.Add(item.Key);
                }
            }

            List<string> arr = new List<string>(keysToRemove);
            for (int i = arr.Count - 1; i >= 0; i--)
            {
                List<LoadBundleCallBackInfo> lstTemp = dicCallBackList[arr[i]];
                for (int j = lstTemp.Count - 1; j >= 0; j--)
                {
                    string aName = lstTemp[j].bundleInfo.abNameWithHash;
                    if (dicException.ContainsKey(aName))
                    {
                        lstTemp[j].callBack(null);
                    }
                    else
                    {
                        lstTemp[j].callBack(GetLoadedAssetBundle(aName));
                    }
                }
                dicCallBackList.Remove(arr[i]);
            }
        }

        /// <summary>
        /// 获取assetbundle路径
        /// </summary>
        /// <param name="abNameWithHash"></param>
        /// <returns></returns>
        
        private string GetABPath(string abNameWithHash)
        {
#if UNITY_IOS
            if (IsLocalRes(abNameWithHash))
            {
                return ApplicationPath.FileStreamingAssetsABPath + abNameWithHash;
            }
#endif
            return ApplicationPath.ABCachePath + abNameWithHash;
        }

        /// <summary>
        /// 边玩变下资源是否存在
        /// </summary>
        /// <param name="abNameWithHash"></param>
        /// <returns></returns>
        private bool IsDownloadABExist(string abNameWithHash)
        {
            string path = ApplicationPath.ABCachePath + abNameWithHash;
#if !UNITY_IOS
            return File.Exists(path) && !DownloadComponent.Instance.IsDownloading(abNameWithHash);
#else
            return File.Exists(path) || IsLocalRes(abNameWithHash);
#endif
        }

        /// <summary>
        /// ios判断本地资源
        /// </summary>
        /// <param name="abNameWithHash"></param>
        /// <returns></returns>
        private bool IsLocalRes(string abNameWithHash)
        {
#if HUpdate
            return DownloadComponent.Instance.LocalBundleInfosWithHash.ContainsKey(abNameWithHash);
#endif
            return VersionComponent.Instance.CdnBundleInfosWithHash.ContainsKey(abNameWithHash);
        }

        /// <summary>
        /// 卸载单场景使用bundle，过场景调用
        /// </summary>
        public void UnloadSceneOnlyBundle()
        {
            keysToRemove.Clear();
            foreach (var item in dicLoadedBundle)
            {
                if ((item.Value.options & LoadOptions.SceneOnly) == LoadOptions.SceneOnly)
                {
                    keysToRemove.Add(item.Key);
                }
            }
            for (int i = keysToRemove.Count - 1; i >= 0; i--)
            {
                Unload(VersionComponent.Instance.CdnBundleInfosWithHash[keysToRemove[i]]);
            }
        }
        /// <summary>
        /// 卸载bundle,现在统一用lz4格式bundle，bundle都不卸载
        /// </summary>
        /// <param name="abNameWithoutHash"></param>
        public void Unload(string abNameWithoutHash,bool unloadType = false)
        {
            AssetBundleInfo abInfo = GetAssetBundleInfo(abNameWithoutHash);
            if (null != abInfo)
            {
                if (dicLoadedBundle.ContainsKey(abInfo.abNameWithHash))
                {
                    UnloadInternal(abInfo.abNameWithHash, unloadType);
                    if (null != abInfo.de)
                    {
                        for (int i = abInfo.de.Length - 1; i >= 0; i--)
                        {
                            UnloadInternal(abInfo.de[i], unloadType);
                        }
                    }
                }                
            }        
        }
        private void UnloadInternal(string abName, bool unloadType = false)
        {
            LoadedAssetBundle dlTemp = GetLoadedAssetBundle(abName);
            if (null != dlTemp)
            {
                string abNameWithHash = dlTemp.abNameWithHash;
                SubBundleRef(abNameWithHash);

                dlTemp.refCount--;
                //if (dlTemp.refCount <= 0)
                if (bundleRef[abNameWithHash] <= 0)
                {
                    //Debug.LogError("unload" + abName);
                    dlTemp.assetBundle.Unload(unloadType);
                    dicLoadedBundle.Remove(abName);
                }
            }
        }
    }
}
